/*****************************************************************************
* File Name: USBFS_HID_interface.c
* Version: 1.0
*
* Description: 
* Receives Data from USB and send to PRoC BLE over UART and Receives data 
* from PRoC BLE over UART and sends to USB.
*
* Owner:
* SIRK
*    
* Related Document:
*
*
* Hardware Dependency:
*
* Hardware Dependency:
* CY5670 CySmart USB Dongle
*
* Code Tested With:
* 1. PSoC Creator 3.1 
* 2. ARM GCC 4.8.4
*
******************************************************************************
* Copyright (2015), Cypress Semiconductor Corporation.
******************************************************************************
* This software is owned by Cypress Semiconductor Corporation (Cypress) and is
* protected by and subject to worldwide patent protection (United States and
* foreign), United States copyright laws and international treaty provisions.
* Cypress hereby grants to licensee a personal, non-exclusive, non-transferable
* license to copy, use, modify, create derivative works of, and compile the
* Cypress Source Code and derivative works for the sole purpose of creating
* custom software in support of licensee product to be used only in conjunction
* with a Cypress integrated circuit as specified in the applicable agreement.
* Any reproduction, modification, translation, compilation, or representation of
* this software except as specified above is prohibited without the express
* written permission of Cypress.
*
* Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, WITH
* REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* Cypress reserves the right to make changes without further notice to the
* materials described herein. Cypress does not assume any liability arising out
* of the application or use of any product or circuit described herein. Cypress
* does not authorize its products for use as critical components in life-support
* systems where a malfunction or failure may reasonably be expected to result in
* significant injury to the user. The inclusion of Cypress' product in a life-
* support systems application implies that the manufacturer assumes all risk of
* such use and in doing so indemnifies Cypress against all charges. Use may be
* limited by and subject to the applicable Cypress software license agreement.
*****************************************************************************/

#include "USB_interface.h"
#include "ADPCMDecoder.h"

/*****************************************************************************
* Local Function Prototypes
*****************************************************************************/

/*****************************************************************************
* MACRO Definition
*****************************************************************************/
/*Mouse data size in the received packet */
#define MOUSE_DATA_SIZE                        (6u)

/*keyboard data size in the received packet */
#define KEYBOARD_DATA_SIZE              (8u)

/*Maximum CDC data size in the received packet, actual size could be lesser. 
    This buffer can be used to contain multiple small CDC OUT packets*/
#define CDC_MAX_DATA_SIZE                     (3500u)

/*Size of header code field in the packet header*/
#define PKT_HEADER_CODE_FIELD_SIZE            (2u)

/*Size of length field in the packet header*/
#define PKT_LENGTH_FIELD_SIZE                 (2u)

/*Size of op-code field in the packet header*/
#define PKT_OPCODE_FIELD_SIZE                 (2u)

/*Size of Total packet header*/
#define PACKET_HEADER_SIZE ((PKT_HEADER_CODE_FIELD_SIZE) + (PKT_LENGTH_FIELD_SIZE) + (PKT_OPCODE_FIELD_SIZE))
#define getCurPktPayloadSize() (Get16ByPtr(&currentPktHeader[PKT_HEADER_CODE_FIELD_SIZE]) - PKT_OPCODE_FIELD_SIZE)

/*Code to be received as start of header packet*/
#define PACKET_HEADER_CODE                (0xA7BDu)

//#define RAW_AUDIO_TO_CDC

/*****************************************************************************
* Global Variable Declarations
*****************************************************************************/
/*Byte index of current packet under process*/
volatile uint32 curPacketIndex = 0u;

/*State m/c for packet detection*/
volatile UART_RX_PKT_STATE pktRxState = STATE_PACKET_HEADER_CODE;

/*Flag to indicate if UART OUT data available from USB Interface*/
volatile bool isCDCOutDataPresent = false;

#ifdef RESET_BUF_LEVEL
volatile uint16 txDataDropSize = 0;
#endif /*RESET_BUF_LEVEL*/

/*****************************************************************************
* Local Global Variable Declarations
*****************************************************************************/
/* One Buffer each for packet received from UART for each endpoint. */
static Endpoint_Packet Ep_Data[MAX_REPORT_TYPE];

/*Intermediate buffers to hold decoded data, before sending to endpoints*/
static uint8 mouseBuff[MOUSE_DATA_SIZE];
static uint8 keyboardBuff[KEYBOARD_DATA_SIZE];
/*This buffer can be used to contain multiple small CDC OUT packets*/
static uint8 cdcBuff[CDC_MAX_DATA_SIZE];

#if defined(USBFS_ENABLE_CDC_CLASS)
/*Data received in the out ep will be transferred to this buffer by DMA.*/
static uint8 CDC_Out_Buffer[USB_MAX_OUT_EP_SIZE];
#endif

/*******************************************************************************
* Function Name: Decompress
********************************************************************************
* Summary:
*  Decompresses the audio data received from UART
*
* Parameters:
*  audioPktByte - ADPCM Compressed byte for two samples
*  dataIndex - Index number of current byte in the audio data packet
*
* Return:
*  None
*
* Theory:
*  None
*
* Side Effects:
*  None
*
*******************************************************************************/
void Decompress(uint16 dataIndex, uint8 audioPktByte)
{
    int16 decodedSample = 0;
    static uint16 audioBufIndex = 0u;

#ifdef AUDIO_NO_HEADER
    if((IN_TRANS_SIZE <= audioBufIndex) || (AUDIO_SYNC_SIZE == dataIndex))
#else
    if((IN_TRANS_SIZE <= audioBufIndex) || (0 == dataIndex))
#endif
    {
        audioBufIndex = 0;
    }

    /*Decode the 4-bit ADPCM code of the lower nibble*/
    decodedSample = ADPCMDecoder(audioPktByte & LOWER_NIBBLE_MASK);

    /*Copy the 16-bit decoded sample into inTransBuffer*/
    inTransBuffer[audioBufIndex++] = (uint8)((uint16)decodedSample & LOWER_BYTE_MASK);
    inTransBuffer[audioBufIndex++] = (uint8)(((uint16)decodedSample >> BIT_8_POSITION) & LOWER_BYTE_MASK);

    /*Decode the 4-bit ADPCM code in the higher nibble*/
    decodedSample = ADPCMDecoder((audioPktByte >> BIT_4_POSITION)& LOWER_NIBBLE_MASK);

    /*Copy the 16- bit decoded sample into inTransBuffer*/
    inTransBuffer[audioBufIndex++] = (uint8)((uint16)decodedSample & LOWER_BYTE_MASK);
    inTransBuffer[audioBufIndex++] = (uint8)(((uint16)decodedSample >> BIT_8_POSITION) & LOWER_BYTE_MASK);
}

/*******************************************************************************
* Function Name: Get16ByPtr
********************************************************************************
*
* Summary:
*  Returns the two-bytes value by the pointer to the LSB.
*
* Parameters:
*  uint8 * ptr - pointer to the LSB of two-bytes data (little endian).
*
* Return:
*  uint16 value - two-bytes data;
*
* Theory:
*  None
*
* Side Effects:
*  None
*
*******************************************************************************/
static uint16 Get16ByPtr(const uint8 ptr[])
{
    return ((uint16) ptr[0u] | ((uint16) ptr[1u] << 8u));
}

/*******************************************************************************
* Function Name: SendToUSB()
********************************************************************************
* Summary:
*  Send data to USB IN endpoint. Maximum of 64 bytes can be sent. If the data
*  is more than 64 bytes, it will be sent in the next call to this function.
*
* Parameters:
*  Endpoint_Packet*
*
* Return:
*  None
*
* Theory:
*  For HID;  mouse and keyboard packets need to maintain the order though they are different endpoints, this is to support combo keys from the device
*  For CDC; Bulk EP polling keeps varying, so to achieve max throughput, the CDC local buffer is kept higher and data will be sent in chunks.
*
* Side Effects:
*  Packets with size higher than allowed will be dropped.
*
*******************************************************************************/
static void SendToUSB(Endpoint_Packet* epData)
{  
    bool isEndPointFree = false;

    /*Send only if endpoint is free, else do nothing and return.*/
    if(USBFS_IN_BUFFER_EMPTY == USBFS_GetEPState(epData->endpoint))
    {
        isEndPointFree = true;

        /*Maintain sequence for HID Mouse & keyboard endpoints, this will ensure HID combo keys are in order*/
        if(MOUSE_EP == epData->endpoint)
        {
            if(epData->datalen > MOUSE_DATA_SIZE)
            {
                /*Reset Data length for next data packet.*/
                epData->datalen = 0;
                /*Reset Offset for next data packet.*/
                epData->offset = 0;

                /*Skip the wrong mouse data if the size is higher*/
                return;
            }
            if (USBFS_IN_BUFFER_EMPTY != USBFS_GetEPState(KEYBOARD_EP))
            {
                isEndPointFree = false;
            }
        }
        else if(KEYBOARD_EP == epData->endpoint)
        {
            if(epData->datalen > KEYBOARD_DATA_SIZE)
            {
                /*Reset Data length for next data packet.*/
                epData->datalen = 0;
                /*Reset Offset for next data packet.*/
                epData->offset = 0;

                /*Skip the wrong keyboard data if the size is higher*/
                return;
            }
            if (USBFS_IN_BUFFER_EMPTY != USBFS_GetEPState(MOUSE_EP))
            {
                isEndPointFree = false;
            }
        }
        else
        {
            /*Nothing to be done here, for other EPs send data immediately*/
        }

        /*Send data if endpoint is free.*/
        if(isEndPointFree)
        {
            /* If data length is more than 64 bytes, send first 64byte.*/
            if(epData->datalen > USB_MAX_IN_EP_SIZE)
            {
                USBFS_LoadInEP(epData->endpoint,
                               (&epData->data[epData->offset]),
                               USB_MAX_IN_EP_SIZE);

                /*Invoke the load IN EP call with NULL to start the DMA transfer*/
                USBFS_LoadInEP(epData->endpoint,
                               USBFS_NULL,
                               USB_MAX_IN_EP_SIZE);

                /*Move Offset to the start of remaining data*/
                epData->offset += USB_MAX_IN_EP_SIZE;
                
                /*Update the pending data.*/
                epData->datalen -= USB_MAX_IN_EP_SIZE;
            }
            else if(0 != epData->datalen)
            {
                /* If data length is less than 64 bytes, send them*/
                USBFS_LoadInEP(epData->endpoint,
                               (&epData->data[epData->offset]),
                               epData->datalen); 

                /*Invoke the load IN EP call with NULL to start the DMA transfer*/
                USBFS_LoadInEP(epData->endpoint,
                               USBFS_NULL,
                               epData->datalen);

                /*Reset Data length for next data packet.*/
                epData->datalen = 0;
                /*Reset Offset for next data packet.*/
                epData->offset = 0;
            }
        }
    }
}

#ifdef RAW_AUDIO_TO_CDC
/*******************************************************************************
* Function Name: SendAudioToCDC()
********************************************************************************
* Summary:
*  Send decompressed raw audio data to CDC interface. Maximum of 64 bytes can be sent at a time. If the data
*  is more than 64 bytes, it will be sent in the next call to this function.
*
* Parameters:
*  Endpoint_Packet*
*
* Return:
*  None
*
* Return:
*  None
*
* Theory:
*  None
*
* Side Effects:
*  None
*
*******************************************************************************/
static void SendAudioToCDC(Endpoint_Packet* epData)
{  
    /*Send only if endpoint is free, else do nothing and return.*/
    if(USBFS_IN_BUFFER_EMPTY == USBFS_GetEPState(CDC_IN_EP))
    {
        /* If data length is more than 64 bytes, send first 64byte.*/
        if(epData->datalen > USB_MAX_IN_EP_SIZE)
        {
            USBFS_LoadInEP(CDC_IN_EP, (&inTransBuffer[epData->offset]), USB_MAX_IN_EP_SIZE);

            /*Invoke the load IN EP call with NULL to start the DMA transfer*/
            USBFS_LoadInEP(CDC_IN_EP, USBFS_NULL, USB_MAX_IN_EP_SIZE);

            /*Move Offset to the start of remaining data*/
            epData->offset += USB_MAX_IN_EP_SIZE;
            
            /*Update the pending data.*/
            epData->datalen -= USB_MAX_IN_EP_SIZE;
        }
        else if(0 != epData->datalen)
        {
            /* If data length is less than 64 bytes, send them*/
            USBFS_LoadInEP(CDC_IN_EP, (&inTransBuffer[epData->offset]), epData->datalen); 

            /*Invoke the load IN EP call with NULL to start the DMA transfer*/
            USBFS_LoadInEP(CDC_IN_EP, USBFS_NULL, epData->datalen);

            /*Reset Data length for next data packet.*/
            epData->datalen = 0;
            /*Reset Offset for next data packet.*/
            epData->offset = 0;
        }
    }
}
#endif

/*******************************************************************************
* Function Name: PktRxStateMachine
********************************************************************************
* Summary:
*  Contains PktRxState m/c to receive data packets of fixed format and discard everything else.
*
* Parameters:
*  None
*
* Return:
*  None
*
* Theory:
*  None
*
* Side Effects:
*  Ep_Data will be modified.
*  Packet will be dropped if the timegap of each byte with-in packet is higher
*
* Note:
*  If the op-code doesnt match with known types it will be sent to CDC by default.
*
*******************************************************************************/
static void PktRxStateMachine(void)
{
    /*RX buffer for Header data, will be included while sending CDC packets*/
    static uint8 currentPktHeader[PACKET_HEADER_SIZE] = {0};
    /*Data Buffer Index for combining multiple CDC packets*/
    static uint16 epPktDataIndex = 0u;
    /*Expected length of the packet's payload*/
    static uint32 expectedPktLength = 0u;
    /*Op-code of current packet under process*/
    static uint16 eventOpCode = 0;

    /* Ep_Data Index to receive packet's payload.*/
    UART_RX_PKT_BUFF_INDEX epIndex = CDC_IN_DATA_INDEX;

    /*Process each byte at a time - for header get the byte and then process the state m/c on it*/
    if(STATE_PACKET_PAYLOAD != pktRxState)
    {   
        /*Receive packet header*/
        currentPktHeader[curPacketIndex++] = (uint8)UART_GetChar();
    }
    else
    {
        /*Copy payload data in the state m/c*/
    }

    /* Restart the timer on each received byte. */
    Timer_PktDecoder_Stop();
    Timer_PktDecoder_Start();

    /* State machine implementation for packet decoding of the received data
     *  over UART.
     *           There are 3 states in this implementation. 
     *           1. Receive Header
     *           2. Receive packet length
     *           3. Receive OpCode 
     *           3. Receive payload 
     *                                                       
     *         |  2 byte  | 2 byte | 2 byte | (Length - 2) byte |
     *         |  HEADER  | LENGTH | OPCODE |      PAYLOAD      |
     *          
     */
    switch(pktRxState) 
    { 
        /* Machine state to handle header code*/
        case STATE_PACKET_HEADER_CODE:
        {
            /*Process first two bytes */
            if( (PKT_HEADER_CODE_FIELD_SIZE == curPacketIndex) &&
                (PACKET_HEADER_CODE == (Get16ByPtr(currentPktHeader ))))
            {               
                isr_PktDecoder_Disable();
                pktRxState = STATE_PACKET_LENGTH;
                isr_PktDecoder_Enable();
            }
            else
            {
                if (curPacketIndex > PKT_HEADER_CODE_FIELD_SIZE)
                {
                    /*Reset the curPacketIndex to start afresh for valid packet.*/
                    curPacketIndex = 0u;
                }
            }
        }
        break;

        /*State machine to handle length*/
        case STATE_PACKET_LENGTH:
        {
            /* Header and Length are received*/
            if((PKT_HEADER_CODE_FIELD_SIZE + PKT_LENGTH_FIELD_SIZE) == curPacketIndex)
            {
                isr_PktDecoder_Disable();
                expectedPktLength = Get16ByPtr(&currentPktHeader[PKT_HEADER_CODE_FIELD_SIZE]);

                /*Check for max size of supported packet, CDC is the largest packet*/
                if((0 != expectedPktLength) && (CDC_MAX_DATA_SIZE > expectedPktLength))
                {
                    pktRxState = STATE_PACKET_OPCODE;
                }
                else
                {
                    /*Reset the state m/c for invalid packet length*/
                    curPacketIndex = 0;
                    pktRxState = STATE_PACKET_HEADER_CODE;
#ifdef DEBUG_W_LED
                    Pin_StatusLED_Write(Pin_StatusLED_Read() ^ 0x01);
#endif /*DEBUG_W_LED*/
                }
                isr_PktDecoder_Enable();
            }
            else
            {
                /*wait until 2 byte length value is received*/
                expectedPktLength = 0;
            }
        }
        break;

        /*State machine to handle op-code*/
        case STATE_PACKET_OPCODE:
        {
            /*Decrement the expected packet length on each byte reception*/
            if(0 != expectedPktLength)
            {
                /*When expectedPktLength will decrement to 0, the next
                           * if block will process the last byte and send to usb*/
                expectedPktLength--;
            }

            /*Packet op-code is received*/
            if(PACKET_HEADER_SIZE == curPacketIndex)
            {
                /*Op-code is followed by Header code and length fields*/
                eventOpCode = Get16ByPtr(&currentPktHeader[PKT_HEADER_CODE_FIELD_SIZE + PKT_LENGTH_FIELD_SIZE]);

                /*Set offset to 0 for Ep_Data[epIndex].data[epPktDataIndex], this index will be used while copying the payload data*/
                epPktDataIndex = 0;
                /*Reset the epindex before decoding the new op-code*/
                epIndex = CDC_IN_DATA_INDEX;

                if(MOUSE_REPORT == eventOpCode)
                {
                    if(sizeof(mouseBuff) < getCurPktPayloadSize())
                    {
                        curPacketIndex = 0;
                        pktRxState = STATE_PACKET_HEADER_CODE;
#ifdef DEBUG_W_LED
                        Pin_StatusLED_Write(Pin_StatusLED_Read() ^ 0x01);
#endif /*DEBUG_W_LED*/
                    }
                    else
                    {
                        epIndex = MOUSE_DATA_INDEX;

                        /*check and push mouse packets if pending*/
                        if(Ep_Data[epIndex].datalen > 0)
                        {
                            SendToUSB(&Ep_Data[epIndex]);
                        }
                    }
                }
                else if(KEYBOARD_REPORT == eventOpCode)
                {
                    if(sizeof(keyboardBuff) < getCurPktPayloadSize())
                    {
                        curPacketIndex = 0;
                        pktRxState = STATE_PACKET_HEADER_CODE;
#ifdef DEBUG_W_LED
                        Pin_StatusLED_Write(Pin_StatusLED_Read() ^ 0x01);
#endif /*DEBUG_W_LED*/
                    }
                    else
                    {
                        epIndex = KEYBOARD_DATA_INDEX;

                        /*check and push kayboard packets if pending*/
                        if(Ep_Data[epIndex].datalen > 0)
                        {
                            SendToUSB(&Ep_Data[epIndex]);
                        }
                    }
                }
                else if(AUDIO_REPORT == eventOpCode)
                {
                    if(AUDIO_DATA_SIZE != getCurPktPayloadSize())
                    {
                        curPacketIndex = 0;
                        pktRxState = STATE_PACKET_HEADER_CODE;
#ifdef DEBUG_W_LED
                        Pin_StatusLED_Write(Pin_StatusLED_Read() ^ 0x01);
#endif /*DEBUG_W_LED*/
                    }
                    else
                    {
                        /*For audio packets no local buffer, payload data will be processed directly*/
                        epIndex = MAX_REPORT_TYPE;
                    }
                }
                else if(AUDIO_CONTROL_STATUS == eventOpCode)
                {
                    /*Audio pipe open/close packet shall contain 1 byte(0u or 1u)*/
                    if(sizeof(uint8) != getCurPktPayloadSize())
                    {
                        curPacketIndex = 0;
                        pktRxState = STATE_PACKET_HEADER_CODE;
#ifdef DEBUG_W_LED
                        Pin_StatusLED_Write(Pin_StatusLED_Read() ^ 0x01);
#endif /*DEBUG_W_LED*/
                    }
                    else
                    {
                        /*For audio packets no local buffer, payload data will be processed directly*/
                        epIndex = MAX_REPORT_TYPE;
                    }
                }
#ifndef AUDIO_NO_HEADER
                else if(AUDIO_SYNC_PACKET == eventOpCode)
                {
                    if(AUDIO_SYNC_SIZE != getCurPktPayloadSize())
                    {
                        curPacketIndex = 0;
                        pktRxState = STATE_PACKET_HEADER_CODE;
#ifdef DEBUG_W_LED
                        Pin_StatusLED_Write(Pin_StatusLED_Read() ^ 0x01);
#endif /*DEBUG_W_LED*/
                    }
                    else
                    {
                        /*For audio packets no local buffer, payload data will be processed directly*/
                        epIndex = MAX_REPORT_TYPE;
                    }
                }
#endif
                else
                {
                    /*All other op-codes should be treated as CDC Packets */
                    epIndex = CDC_IN_DATA_INDEX;
                }
                
                if(CDC_IN_DATA_INDEX == epIndex)
                {
#ifdef DEBUG_W_LED
                    Pin_PowerLED_SetDriveMode(Pin_PowerLED_DM_OD_LO);
#endif /*DEBUG_W_LED*/
                    /*Check If CDC local buffer contains last packet data*/
                    if(0 != Ep_Data[CDC_IN_DATA_INDEX].datalen)
                    {
                        /*Check if new packet can fit into available space of local CDC buffer*/
                        /*Below handling is to support bulk push of multiple packets whenever the USB polls for CDC*/
                        if(CDC_MAX_DATA_SIZE < (Ep_Data[CDC_IN_DATA_INDEX].datalen + Ep_Data[CDC_IN_DATA_INDEX].offset
                                                + Get16ByPtr(&currentPktHeader[PKT_HEADER_CODE_FIELD_SIZE])
                                                + PKT_LENGTH_FIELD_SIZE + PKT_HEADER_CODE_FIELD_SIZE))
                        {
                            /*Get the index of occupied space in the local buffer*/
                            epPktDataIndex = Ep_Data[CDC_IN_DATA_INDEX].datalen + Ep_Data[CDC_IN_DATA_INDEX].offset;
                        }
                        else
                        {
                            /*check and push CDC packets if pending*/
                            if(Ep_Data[CDC_IN_DATA_INDEX].datalen > 0)
                            {
                                SendToUSB(&Ep_Data[CDC_IN_DATA_INDEX]);
                            }

                            /*Overwrite CDC endpoint data if not able to push it to Host*/
                            Ep_Data[CDC_IN_DATA_INDEX].datalen = 0;
                            Ep_Data[CDC_IN_DATA_INDEX].offset = 0;

                            /*Copy the header of current packet being processed from UART rx buffer.*/
                            epPktDataIndex = 0;
                        }
                    }
                    else
                    {
                        /*Local CDC buffer is free, copy the header*/
                        epPktDataIndex = 0;
                    }

                    /*Copy/Append the new packet header to the available free space*/
                    memcpy(&Ep_Data[CDC_IN_DATA_INDEX].data[epPktDataIndex], currentPktHeader, PACKET_HEADER_SIZE);

                    /*Increment the data index by header size, append the payload in "STATE_PACKET_PAYLOAD" state*/
                    epPktDataIndex += PACKET_HEADER_SIZE;
                }

                {
                    isr_PktDecoder_Disable();
                    /*Transit to receive payload state.*/
                    pktRxState = STATE_PACKET_PAYLOAD;
                    isr_PktDecoder_Enable();
                }
            }

            if(0 == expectedPktLength)
            {
                if(CDC_IN_DATA_INDEX == epIndex)
                {
                   /*Send complete packet, including CY Header and packet length*/
                   Ep_Data[epIndex].datalen += Get16ByPtr(&currentPktHeader[PKT_HEADER_CODE_FIELD_SIZE])
                                                   + PKT_LENGTH_FIELD_SIZE + PKT_HEADER_CODE_FIELD_SIZE;
                }
                else if(MAX_REPORT_TYPE > epIndex)
                {
                    /*Send only payload with-out op-code; Op-code is used only to identify proper endpoint for non CDC packets*/
                    Ep_Data[epIndex].datalen = Get16ByPtr(&currentPktHeader[PKT_HEADER_CODE_FIELD_SIZE])
                                                            - PKT_OPCODE_FIELD_SIZE;
                }

                /*Load USB endpoint*/
                SendToUSB(&Ep_Data[epIndex]);
                
                /* Reset the state machine*/
                Timer_PktDecoder_Stop();
                curPacketIndex = 0;
                epPktDataIndex = 0;
                pktRxState = STATE_PACKET_HEADER_CODE;
#ifdef DEBUG_W_LED
                Pin_StatusLED_Write(Pin_StatusLED_Read() ^ 0x01);
#endif /*DEBUG_W_LED*/
            }

        }/*End of case STATE_PACKET_OPCODE*/
        break;

        /*State machine to handle payload*/
        case STATE_PACKET_PAYLOAD:
        {
            /*Stop the timer and start if payload data is pending*/
            Timer_PktDecoder_Stop();

            switch(eventOpCode)
            {
                case MOUSE_REPORT:
                    epIndex = MOUSE_DATA_INDEX;
                    break;

                case KEYBOARD_REPORT:
                    epIndex = KEYBOARD_DATA_INDEX;
                    break;

                case AUDIO_REPORT:
                    if(RxDMADone)
                    {
                        epIndex = MAX_REPORT_TYPE;
                    }
                    else
                    {
                        /*Wait until RX DMA is completed, dont take the data from UART buffer while RX DMA is in progress*/
                        return;
                    }
                    break;

                case AUDIO_SYNC_PACKET:
                case AUDIO_CONTROL_STATUS:
                    epIndex = MAX_REPORT_TYPE;
                    break;

                default:
                    epIndex = CDC_IN_DATA_INDEX;
                    break;
            }

            /*Check and copy payload data if available in UART buffer*/
            if((0 < expectedPktLength) && (0 < UART_GetRxBufferSize()))
            {
                if(AUDIO_CONTROL_STATUS == eventOpCode)
                {
                    /*Get the one byte data from UART rx buffer.*/
                    audioPipeOpen = ((uint8)UART_GetChar() == (uint8)0u)?false:true;
                }
#ifndef AUDIO_NO_HEADER
                else if(AUDIO_SYNC_PACKET == eventOpCode)
                {
                    /*Audio sync packet contains fixed number of bytes*/
                    switch(epPktDataIndex++)
                    {
                        case ADPCM_PREV_INDEX:
                            /*Extract the quantizer index into lookup table from received audio packet*/
                            state.prevIndex = (uint8)UART_GetChar();
                            break;

                        case ADPCM_PREV_SAMPLE_BYTE_0:
                            /*Extract the 16 bit sample Higher byte*/
                            state.prevSample = (((int16)UART_GetChar()) << BIT_8_POSITION);
                            break;

                        case ADPCM_PREV_SAMPLE_BYTE_1:
                            /*Extract the 16 bit sample lower byte*/
                            state.prevSample |= (int16)UART_GetChar();
#ifdef RESET_BUF_LEVEL
                            /*Audio Tx device has dropped the data, 
                                                so increase the buffer content level on each sync to avoid USB underflow*/
                            txDataDropSize = IN_SENDLIMIT;
#endif /*RESET_BUF_LEVEL*/
                            break;
                    }
                }
#endif
                else if(AUDIO_REPORT == eventOpCode)
                {
#ifdef AUDIO_NO_HEADER
                    switch(epPktDataIndex++)
                    {
                        case ADPCM_PREV_INDEX:
                            /*Extract the quantizer index into lookup table from received audio packet*/
                            state.prevIndex = UART_GetChar();
                            break;

                        case ADPCM_PREV_SAMPLE_BYTE_0:
                            /*Extract the 16 bit sample Higher byte*/
                            state.prevSample = (((int16)UART_GetChar()) << BIT_8_POSITION);
                            break;

                        case ADPCM_PREV_SAMPLE_BYTE_1:
                            /*Extract the 16 bit sample lower byte*/
                            state.prevSample |= (int16)UART_GetChar();
                            /*Reset the audio buffer index for each new packet*/
                            break;
                            default:
#endif
                            Decompress(epPktDataIndex, UART_GetChar());
#ifndef AUDIO_NO_HEADER
                            /*For packets with-out audio header, increment the index*/
                            epPktDataIndex++;
#else
                            break;
                    }
#endif
                }
                else
                {
                    /*Receive packet payload*/
                    Ep_Data[epIndex].data[epPktDataIndex++] =  UART_GetChar();
                }
                /*When expectedPktLength will decrement to 0, the next
                           * if block will process the last byte and send to USB*/
                expectedPktLength--;
            }

            /* Complete packet recieved, reset the state m/c*/
            if(0 == expectedPktLength)
            {
                if(CDC_IN_DATA_INDEX == epIndex)
                {
#ifdef DEBUG_W_LED
                    Pin_PowerLED_SetDriveMode(Pin_PowerLED_DM_OD_LO);
#endif /*DEBUG_W_LED*/
                   /*Send complete packet, including CY Header and packet length*/
                   Ep_Data[epIndex].datalen += Get16ByPtr(&currentPktHeader[PKT_HEADER_CODE_FIELD_SIZE])
                                                      + PKT_LENGTH_FIELD_SIZE + PKT_HEADER_CODE_FIELD_SIZE;
                }
                else if(MAX_REPORT_TYPE > epIndex)
                {
                    /*Send only payload with-out op-code; Op-code is used only to identify proper endpoint for non CDC packets*/
                    Ep_Data[epIndex].datalen = Get16ByPtr(&currentPktHeader[PKT_HEADER_CODE_FIELD_SIZE])
                                                           - PKT_OPCODE_FIELD_SIZE;
                }

                /*audio packet has been received.*/
                if(AUDIO_REPORT == eventOpCode)
                {
#ifdef RAW_AUDIO_TO_CDC
                    /*Set the EP to CDC and set the data to be raw transaction size*/
                    epIndex = CDC_IN_DATA_INDEX;
                    Ep_Data[epIndex].datalen = sizeof(inTransBuffer);
                    Ep_Data[epIndex].offset = 0;

                    while(0 < Ep_Data[epIndex].datalen)
                    {
                        SendAudioToCDC(&Ep_Data[epIndex]);
                    }

#else
                    if(inPlaying)
                    {
#ifdef RESET_BUF_LEVEL
                        /*Flush off the circular buffer one first sample after sync*/
                        if(0 != txDataDropSize)
                        {
                            memset(inBuffer, 0, sizeof(inBuffer));  
                        }
#endif /*RESET_BUF_LEVEL*/
                        /*Check for available space in Circ Buffer and load the new data, if no space drop the current data*/
                        Trigger_rXDMA();
                    }
#endif
                }
                else if(MAX_REPORT_TYPE > epIndex)
                {
                    /*Load USB endpoint*/
                    SendToUSB(&Ep_Data[epIndex]);
                }

                /* Reset the state machine*/
                Timer_PktDecoder_Stop();
                curPacketIndex = 0;
                epPktDataIndex = 0;
                pktRxState = STATE_PACKET_HEADER_CODE;
            }
            else if(0 == UART_GetRxBufferSize())
            {
                /*Run the timer as packet is still pending while UART block doesnt have new data*/
                Timer_PktDecoder_Start();
            }
        }/*End of case STATE_PACKET_PAYLOAD:*/
        break;

        default:
            /* invalid pktRxState. Reset the state*/
            curPacketIndex = 0;
            pktRxState = STATE_PACKET_HEADER_CODE;
#ifdef DEBUG_W_LED
            Pin_StatusLED_Write(Pin_StatusLED_Read() ^ 0x01);
#endif /*DEBUG_W_LED*/
        break;
    }
}

/*******************************************************************************
* Function Name: HandleUSBComm
********************************************************************************
* Summary:
* This function handles comm for sending data received by UART to USB.
*
* Parameters:
*  None
*
* Return:
*  None
*
* Theory:
*  None
*
* Side Effects:
*  Clears the UART SW buffer if overflow is detected.
*
* Note:
*
*******************************************************************************/
void HandleUSBComm(void)
{
    uint8 reportIndex; 

    /*Clear RX buffer, if overflow is detected*/
    if(UART_RX_STS_SOFT_BUFF_OVER == UART_ReadRxStatus())
    {
        UART_ClearRxBuffer();
    }

    if(0 < UART_GetRxBufferSize())
    {
        /*Process each RX byte*/
        PktRxStateMachine();
    }

    /*Process all pending buffers to be sent to USB*/
    for(reportIndex = 0; reportIndex < MAX_REPORT_TYPE; reportIndex++)
    {
        /*Send to USB if any pending data.*/
        if(Ep_Data[reportIndex].datalen > 0)
        {
            SendToUSB(&Ep_Data[reportIndex]);
        }
    }
}

/*******************************************************************************
* Function Name: USBCommInit
********************************************************************************
* Summary:
* This function initializes rx state machine to its initial state and also
* initializes local buffers meant for receiving packets.
*
* Parameters:
*  None
*
* Return:
*  None
*
* Theory:
*  None
*
* Side Effects:
* Modifies  pktRxState and Ep_Data
*
* Note:
*
*******************************************************************************/
void USBCommInit(void)
{
    uint8 numReport = 0;

    /*Initialize state machine*/
    pktRxState =  STATE_PACKET_HEADER_CODE;
    curPacketIndex = 0;

    /*Initialize local structure meant for holding received packet 
        *until endpoint is free*/   
    for(numReport = 0;  numReport < MAX_REPORT_TYPE; numReport++)
    {
        Ep_Data[numReport].datalen = 0;
        Ep_Data[numReport].offset = 0;
    }

    /*Assign endpoints*/
    Ep_Data[MOUSE_DATA_INDEX].endpoint = MOUSE_EP;
    Ep_Data[KEYBOARD_DATA_INDEX].endpoint = KEYBOARD_EP;
    Ep_Data[CDC_IN_DATA_INDEX].endpoint = CDC_IN_EP;

    /*Assign buffers*/
    Ep_Data[MOUSE_DATA_INDEX].data = mouseBuff;
    Ep_Data[KEYBOARD_DATA_INDEX].data = keyboardBuff;
    Ep_Data[CDC_IN_DATA_INDEX].data = cdcBuff;
}

#if defined(USBFS_ENABLE_CDC_CLASS)
/*******************************************************************************
* Function Name: CheckLine
********************************************************************************
* Summary:
* This function Checks for line change, Reconfigures the UART clock to 
* match host baud rate
*
* Parameters:
*  None
*
* Return:
*  None
*
* Theory:
*  None
*
* Side Effects:
* None
*
*******************************************************************************/
static void CheckLine(void) 
{
    uint32 dDTERate;
    uint16 wDivider;

    if(USBFS_IsLineChanged())
    {
        /* Get Baud Rate */
        dDTERate = USBFS_GetDTERate();

        /* Check for Baud Rate Upper Limit */
        if(dDTERate > BAUDRATE921600)
        {
            dDTERate  = BAUDRATE921600;
        }

        /* Check for Baud Rate Lower Limit */
        if(dDTERate < BAUDRATE1200)
        {
            dDTERate  = BAUDRATE1200;
        }
        /* Sets the required Clock divider for UART */
        /*Disable the UART baud rate configuration from Host*/
#ifdef EN_GENERIC_BAUDRATE_CONFIG
        switch(dDTERate)
        {
            case BAUDRATE921600:
                wDivider = DIVIDER1000000;
                break;
            case BAUDRATE460800:
                wDivider = DIVIDER500000;
                break;
            case BAUDRATE230400:
                wDivider = DIVIDER250000;
                break;
            case BAUDRATE115200:
                wDivider = DIVIDER115200;
                break;
            case BAUDRATE57600:
                wDivider = DIVIDER57600;
                break;
            case BAUDRATE38400:
                wDivider = DIVIDER38400;
                break;
            case BAUDRATE19200:
                wDivider = DIVIDER19200;
                break;
            case BAUDRATE9600:
                wDivider = DIVIDER9600;
                break;
            case BAUDRATE4800:
                wDivider = DIVIDER4800;
                break;
            case BAUDRATE2400:
                wDivider = DIVIDER2400;
                break;
            case BAUDRATE1200:
                wDivider = DIVIDER1200;
                break;
            default:
                wDivider = DIVIDER500000;
                break;
        }
#else
        /* Hard-coding UART Baud-rate, To support both Audio & CySmart operations
                  the UART configuration is hard-coded as per PRoC-BLE*/
        wDivider = DIVIDER500000;
#endif //EN_GENERIC_BAUDRATE_CONFIG

        /* Stop UART for new Clock */
        UART_Stop();

        Clock_1_SetDivider(wDivider-1);

        /* Restart UART */
        UART_Start();
        UART_ClearRxBuffer();
        UART_ClearTxBuffer();

        /* Enable USB out end point */
        USBFS_EnableOutEP(UART_OUT_EP);
    }
}

/*******************************************************************************
* Function Name: UARTTransmit()
********************************************************************************
* Summary:
* This function handles the USB-UART interface.
* It handles,
* a) Selection of UART clock based on host baud rate
* b) Transmit host packet to UART TX
*
* Parameters:
*  None
*
* Return:
*  None
*
* Theory:
*  None
*
* Side Effects:
* Clears the isCDCOutDataPresent flag
*
*******************************************************************************/
void UARTTransmit (void)
{
    /* Check for Line configuration change, Reconfigure UART based on host parameters */
    CheckLine();

    /* USB to UART Transmit */
    if(isCDCOutDataPresent)
    {
        UART_PutArray((const uint8*)CDC_Out_Buffer, USBFS_GetEPCount(UART_OUT_EP));
        isCDCOutDataPresent = false;
        USBFS_EnableOutEP(UART_OUT_EP);
    }
}

/*******************************************************************************
* Function Name: USBUARTInit()
********************************************************************************
* Summary:
* Starts the UART, sets Tx Pin to HiZ to save power during enumeration
*
* Parameters:
*  None
*
* Return:
*  None
*
* Theory:
*  None
*
* Side Effects:
*  None
*
*******************************************************************************/
void USBUARTInit (void)
{
    USBFS_ReadOutEP(UART_OUT_EP, (uint8*)CDC_Out_Buffer, sizeof(CDC_Out_Buffer));
    isCDCOutDataPresent = false;
    Pin_UART_Tx_SetDriveMode(Pin_UART_Tx_DM_DIG_HIZ);
}

/*******************************************************************************
* Function Name: USBUARTStart()
********************************************************************************
* Summary:
* Initializes CDC Interface, Sets Tx Pin to Strong Drive
*
* Parameters:
*  None
*
* Return:
*  None
*
* Theory:
*  None
*
* Side Effects:
*  None
*
*******************************************************************************/
void USBUARTStart (void)
{
    /* Initialize CDC Interface for USB-UART Bridge */
    USBFS_CDC_Init();
    Pin_UART_Tx_SetDriveMode(Pin_UART_Tx_DM_STRONG);
}
#endif

/* [] END OF FILE */

